#include <iostream>
#include <cmath>
#include <ctime>
#include <getopt.h>
#include <string>
#include <cstring>

using namespace std;

/* class Angle */
enum AngleFormat {DEG, RAD, DMS};

class Angle {
private:
	float value;
	AngleFormat format;

	// constructing angle format from string
	AngleFormat angle_format_from_string(const string& fmt_str) const;

	// constructing tm struct from string
	tm tm_from_string(const string& tm_str) const;
public:
	static const int MINUTES_PER_DEGREE = 60;
	static const int SECONDS_PER_DEGREE = 3600;

	// create object from time string and format string
	Angle(const string& time_str, const string& fmt);
	
	// copy constructor
	Angle(const Angle& a);

	// destructor
	virtual ~Angle() {};
	
	// output operator
	friend ostream& operator<<(ostream& os, const Angle& a);

	// assignment operator
	Angle& operator=(const Angle& a);

	// converting the angle value from degrees to degrees-minutes-seconds	
	void deg2dms(int& d, int& m, float& s) const;
	
	// converting the angle value from radians to degrees
	float rad2deg(float rad) const { return (rad * 180.0f / M_PI); }

	// converting the angle value from degrees to radians
	float deg2rad(float deg) const { return (deg * M_PI / 180.0f); }
};

tm Angle::tm_from_string(const string& tm_str) const {
	string tm_fmt[] = {"PM", "pm", "Pm", "pM", "AM", "am", "Am", "aM"};
	size_t found;
	for(int i = 0; i < 8; ++i) {
		found = tm_str.find(tm_fmt[i]);
		if(found != string::npos) break;
	}
	tm t;
	memset(&t, 0, sizeof(tm));
	if(found != string::npos) {
		strptime(tm_str.c_str(), "%I:%M %p", &t);		
	} else {
		strptime(tm_str.c_str(), "%R", &t);
	}
	return t;
}

AngleFormat Angle::angle_format_from_string(const string& fmt_str) const {
	if(fmt_str == "RAD" || fmt_str == "rad") {
		return RAD;
	} else if(fmt_str == "DMS" || fmt_str == "dms") {
		return DMS;
	} else return DEG;
}

Angle::Angle(const string& tm_str, const string& fmt_str) {
	format = angle_format_from_string(fmt_str);
	tm t = tm_from_string(tm_str);
	// calculate the angle between the hour hand
	value = fabs(fmod((30 * t.tm_hour + 0.5 * t.tm_min), 360.0) - 6 * t.tm_min);
}

void Angle::deg2dms(int& d, int& m, float& s) const {
	d = static_cast<int>(value);
	float tmp = fabs(d - value);
	m = static_cast<int>(tmp * MINUTES_PER_DEGREE);
	s = tmp * SECONDS_PER_DEGREE - m * MINUTES_PER_DEGREE;
}

Angle& Angle::operator=(const Angle& a) {
	if(this == &a) { // checking for self-assignment
		return *this;
	}
	value = a.value;
	format = a.format;	
	return *this;
}

Angle::Angle(const Angle& a) {
	value = a.value;
	format = a.format;
}

ostream& operator<<(ostream& os, const Angle& a) {
	switch(a.format) {
	case DMS:
		int degrees;
		int minutes;
		float seconds;
		a.deg2dms(degrees, minutes, seconds);
		os << degrees << "." << minutes << "\'" << seconds << "\'\'";
		break;
	case DEG:
		os << a.value << " DEG";
		break;
	case RAD:
		os << a.deg2rad(a.value) << " RAD";
		break;
	}
	return os;
}

/* end of class Angle */

// parse the options of command line
// time_str - the time in 12-hours (a.g. 09:00 PM) or 24-hours (a.g. 21:00) format
// angle_fmt - the angle format (DEG or RAD or DMS)
void parse_options(int argc, char* const argv[], string& time_str, string& angle_fmt){
	int c;
	while(1) {
		static struct option long_options[] = {
			{"time", required_argument, 0, 't'},
			{"angle", required_argument, 0, 'a'},
		};
		int option_index = 0;
		c = getopt_long(argc, argv, "t:a:", long_options, &option_index);	
		if(c == -1) break;
		switch(c) {
			case 't':
				time_str = string(optarg);
				break;
			case 'a':
				angle_fmt = string(optarg);
				break;
			default: break;
				
		}
	}
}

int main(int argc, char* argv[]) {
	if(argc == 1) {
		cout << "Calculate the angle between the hour hands" << endl;
		cout << "Usage: time2angle --time \"<time string>\" --angle <DEG | RAD | DMS>"<< endl;
		cout << "The time string is string in 12-hours (a.g. \"09:00 PM\") or 24-hours (a.g. 21:00) format" << endl;
		cout << "All arguments is case-insensitive" << endl;
		return 0;
	}
	string time_str;
	string angle_fmt;
	parse_options(argc, argv, time_str, angle_fmt);
	cout << Angle(time_str, angle_fmt) << endl;
	return 0;
}
